package ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.bitwise.visitors;

import java.util.ArrayList;
import java.util.List;

import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.Expresion;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.bitwise.And;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.bitwise.False;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.bitwise.Not;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.bitwise.Or;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.bitwise.True;

/**
 * Aplica los teoremas del algebra de conmutacion con
 * una variable para reducir una expresion
 * 
 * @author Juan F. Codagnone
 * @since Apr 1, 2010
 */
public class BitwiseExpresionEditor implements Expresion.VisitorCallback {
    private static final Expresion T = new True();
    private static final Expresion F = new False();

    /** @see Expresion.VisitorCallback#notify(Expresion) */
    public final Expresion notify(final Expresion expr) {
        Expresion ret = expr;

        if (expr instanceof Not) {
            // T4: X'' = X
            final Expresion child = expr.iterator().next();
            if (child instanceof Not) {
                ret = child.iterator().next();
            } else if (child instanceof True) {
                ret = F;
            } else if (child instanceof False) {
                ret = T;
            }
        } else if (expr instanceof And || expr instanceof Or) {
            // and o or con argumentos similares
            boolean equals = true;
            Expresion tmp = null;

            boolean trueArgument = false;
            boolean falseArgument = false;

            for (final Expresion child : expr) {
                if (tmp == null) {
                    tmp = child;
                } else {
                    equals &= tmp.equals(child);
                }
                if (child.equals(T)) {
                    trueArgument = true;
                } else if (child.equals(F)) {
                    falseArgument = true;
                }
            }

            if (equals) {
                // T3: X + X = X
                // T3' = X . X = X
                ret = tmp;
            } else if (trueArgument) {
                if (expr instanceof And) {
                    // T1' X . 1 = X
                    final List<Expresion> l = new ArrayList<Expresion>();
                    for (final Expresion child : expr) {
                        if (!child.equals(T)) {
                            l.add(child);
                        }
                    }
                    if (l.size() == 1) {
                        ret = l.iterator().next();
                    }
                } else if (expr instanceof Or) {
                    // T2 = X + 1 = 1
                    ret = T;
                }
            } else if (falseArgument) {
                if (expr instanceof And) {
                    // T2' X . 0 = 0
                    ret = F;
                } else if (expr instanceof Or) {
                    // T1 = X + 0 = X
                    final List<Expresion> l = new ArrayList<Expresion>();
                    for (final Expresion child : expr) {
                        if (!child.equals(F)) {
                            l.add(child);
                        }
                    }
                    if (l.size() == 1) {
                        ret = l.iterator().next();
                    }
                }
            } else {
                // T5 y T5'
                final List<Expresion> l = new ArrayList<Expresion>();
                for (final Expresion child : expr) {
                    l.add(child);
                }
                if (l.size() == 2) {
                    final Expresion e1 = l.get(0);
                    final Expresion e2 = l.get(1);

                    if ((e1 instanceof Not && ((Not) e1).iterator().next()
                            .equals(e2))
                            || (e2 instanceof Not && ((Not) e2).iterator()
                                    .next().equals(e1))) {
                        ret = (expr instanceof And) ? F : T;
                    }
                }
            }
        }

        return ret;
    }
}